/**
 * PHOSIDE: PHosphorylation Site IDentification Engine.
 * Copyright 2009 Digital Biology Lab, University of Missouri.
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 3 of the License, or (at your option) any later
 * version. <p/> This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more
 * details.
 */

package phoside.knn;

import java.io.Serializable;

import java.util.List;

import phoside.data.feature.model.Instance;

import phoside.util.MultiTreeMap;
import phoside.util.ListUtil;

/**
 *
 * @author gjj
 */
public class KNNExtractorImpl implements KNNExtractor, Serializable {
    private static final long serialVersionUID = 1515666963108117172L;

    protected final ClosenessCalculator closenessCalculator;
    protected List<Integer> sortedIndices;
    protected List<Double> sortedCloseness;
    
    public KNNExtractorImpl(final ClosenessCalculator closenessCalculator) {
        this.closenessCalculator = closenessCalculator;
    }

    public ClosenessCalculator getClosenessCalculator() {
        return closenessCalculator;
    }

    /**
     * Extract K nearest neighbors of instance from instances
     * @param instance
     * @param instances
     * @param K
     * @return
     */
    public boolean getNNsSortedByDistance(final Instance instance,
                                   final List<Instance> instances) {
        if (instance==null || instances==null) {
            throw new NullPointerException();
        }

        int N = instances.size();

        MultiTreeMap<Double,Integer> mmap = new MultiTreeMap<Double,Integer>();

        for (int i=0; i<N; i++) {
            Instance ins = instances.get(i);

            double closeness;
            if (instance.getInstanceTag().equals(ins.getInstanceTag())) {
                closeness = 0;
            } else {
                closeness = closenessCalculator.calculate(ins, instance);
            }
            mmap.add(1-closeness, i); // distance = 1-closeness
        }

        sortedIndices = mmap.allValues();
        sortedCloseness = mmap.allKeys();

        return true;
    }

    /**
     *
     * @return
     */
    public List<Integer> getKNNIndex(final int K) {
        if (sortedIndices==null) {
            throw new java.lang.IllegalStateException();
        }
        return ListUtil.subList(sortedIndices, 0, K);
    }

    /**
     *
     * @return
     */
    public List<Integer> getKNNIndex() {
        if (sortedIndices==null) {
            throw new java.lang.IllegalStateException();
        }
        return sortedIndices;
    }

    /**
     *
     * @return
     */
    public List<Double> getKNNDistance(final int K)  {
        if (sortedIndices==null) {
            throw new java.lang.IllegalStateException();
        }
        return ListUtil.subList(sortedCloseness, 0, K);
    }

    /**
     *
     * @return
     */
    public List<Double> getKNNDistance()  {
        if (sortedIndices==null) {
            throw new java.lang.IllegalStateException();
        }

        return sortedCloseness;
    }

}
